<?php

namespace yunj\app\admin\validate\route;

use yunj\app\admin\enum\IsSystem;
use yunj\app\admin\enum\route\RouteDataType;
use yunj\app\admin\enum\route\RoutePageFormType;
use yunj\app\admin\enum\State;
use yunj\app\admin\enum\TableBuilderEvent;
use yunj\app\admin\service\route\RequestAuthService;
use yunj\app\admin\service\route\page\RouteListService;
use yunj\app\admin\service\route\RouteRequestService;
use yunj\app\admin\service\route\RouteService;
use yunj\app\admin\validate\Validate;
use yunj\core\builder\YunjForm;
use yunj\app\admin\service\route\page\RouteAddService;
use yunj\app\admin\service\route\page\RouteEditService;

class RouteValidate extends Validate {

    /**
     * @return RouteAddService
     */
    private function getAddService(): RouteAddService {
        return app(RouteAddService::class);
    }

    /**
     * @return RouteEditService
     */
    private function getEditService(): RouteEditService {
        return app(RouteEditService::class);
    }

    /**
     * 判断中间件
     * @param $value
     * @param string $rule
     * @param array $data
     * @param string $field
     * @param string $title
     * @return bool|string
     */
    protected function isMiddleware($value, string $rule = "", array $data = [], string $field = "", string $title = "") {
        foreach ($value as $i => $item) {
            if (!RouteService::checkMiddlewareItem($item)) {
                return $this->packageError($field, $title, "第{$i}项配置错误");
            }
        }
        return true;
    }

    protected function handleData(array &$data, $scene): void {
        switch ($scene) {
            case 'RouteListEvent':
                $this->handleDataByRouteListEvent($data);
                break;
            case 'RouteAddFormSubmit':
                $this->handleDataByRouteAddFormSubmit($data);
                break;
            case 'RouteEditFormSubmit':
                $this->handleDataByRouteEditFormSubmit($data);
                break;
        }
    }

    private function handleDataByRouteListEvent(&$data): void {
        $event = $data['event'];
        if (TableBuilderEvent::isValue($event) && ($eventObj = TableBuilderEvent::byValue($event)) && $eventObj->isStateEvent()) {
            // 处理ids
            $dataTypes = [];
            foreach ($data['ids'] as $id) {
                list($type, $id) = explode('_', $id);
                if (!RouteDataType::isValue($type)) {
                    throw_error_json('数据异常，请刷新页面后重试');
                }
                $dataTypes[$type] = $dataTypes[$type] ?? [];
                if (!isset($dataTypes[$type]['typeObj'])) {
                    $dataTypes[$type]['typeObj'] = RouteDataType::byValue($type);
                }
                $dataTypes[$type]['ids'] = $dataTypes[$type]['ids'] ?? [];
                $dataTypes[$type]['ids'][] = $id;
            }
            if (!$dataTypes) {
                throw_error_json('请选择要操作的数据');
            }
            // 判断是否有系统数据
            foreach ($dataTypes as $type => $v) {
                /** @var RouteDataType $typeObj */
                $typeObj = $v['typeObj'];
                $model = $typeObj->getModel();
                $hasSys = $model->getOwnCount([
                    ['id', 'in', $v['ids']],
                    ['system_key', 'is not null'],
                ]);
                if ($hasSys) {
                    throw_error_json('系统配置' . $typeObj->getTitle() . '不可' . $eventObj->getDesc());
                }
            }
            $data['eventObj'] = $eventObj;
            $data['dataTypes'] = $dataTypes;
        }
    }

    private function handleDataByRouteAddFormSubmit(&$data): void {
        $service = $this->getAddService();
        $typeObj = $service->getTypeObj();
        $parentId = $service->getParentId();
        $dbData = [
            'create_time' => time(),
            'last_update_time' => time(),
        ];
        switch ($typeObj->getValue()) {
            case RouteDataType::GROUP:
                $dbData['pid'] = $parentId ?: 0;
                break;
            case RouteDataType::ROUTE:
                $dbData['group_id'] = $parentId ?: 0;
                break;
            case RouteDataType::REQUEST:
                $dbData['route_id'] = $parentId;
                break;
        }
        $data['dbData'] = $dbData;
        $this->handleDataByRouteFormSubmit($data, $typeObj);
    }

    private function handleDataByRouteEditFormSubmit(&$data): void {
        $service = $this->getEditService();
        $typeObj = $service->getTypeObj();
        $dbData = [
            'id' => $service->getId(),
            'last_update_time' => time(),
        ];
        // 系统配置
        if ($service->isSystem()) {
            if (in_array($typeObj->getValue(), [RouteDataType::GROUP, RouteDataType::ROUTE])) {
                // 系统配置的分组和路由允许调整自定义中间件
                $dbData['middleware'] = $data['middleware'] ? json_encode($data['middleware'], JSON_UNESCAPED_UNICODE) : '';
                $data['dbData'] = $dbData;
                $data['model'] = $typeObj->getModel();
            } else {
                // 系统配置的路由请求不允许修改
                throw_error_json('系统' . $typeObj->getTitle() . '不允许编辑');
            }
            return;
        }
        // 非系统配置
        switch ($typeObj->getValue()) {
            case RouteDataType::GROUP:
                $dbData['pid'] = $data['pid'] ?: 0;
                break;
            case RouteDataType::ROUTE:
                $dbData['group_id'] = $data['group_id'] ?: 0;
                break;
            case RouteDataType::REQUEST:
                $dbData['route_id'] = $data['route_id'];
                break;
        }
        $data['dbData'] = $dbData;
        $this->handleDataByRouteFormSubmit($data, $typeObj);
    }

    private function handleDataByRouteFormSubmit(&$data, RouteDataType $typeObj) {
        $type = $typeObj->getValue();
        $model = $typeObj->getModel();
        // dbData
        switch ($type) {
            case RouteDataType::GROUP:
                $data['dbData'] += [
                    'name' => $this->checkRouteRule($data['name'], '分组名称'),
                    'namespace' => handle_route_namespace($data['namespace']),
                    'middleware' => $data['middleware'] ? json_encode($data['middleware'], JSON_UNESCAPED_UNICODE) : '',
                    'desc' => $data['desc'],
                ];
                break;
            case RouteDataType::ROUTE:
                $data['dbData'] += [
                    'name' => $data['name'] ?: null,
                    'desc' => $data['desc'],
                    'rule' => $this->checkRouteRule($data['rule'], '路由规则'),
                    'route' => handle_route_namespace($data['route']),
                    'middleware' => $data['middleware'] ? json_encode($data['middleware'], JSON_UNESCAPED_UNICODE) : '',
                ];
                break;
            case RouteDataType::REQUEST:
                $data['dbData'] += [
                    'desc' => $data['desc'],
                    'method' => $data['method'],
                    'enable_login_auth' => $data['enable_login_auth'],
                ];
                // 必要参数格式转换
                $requireParams = $data['require_params'];
                if ($requireParams) {
                    $dbRequireParams = [];
                    foreach ($requireParams as $item) {
                        $dbRequireParams[$item['key']] = $item['value'];
                    }
                }
                $data['dbData']['require_params'] = !empty($dbRequireParams) ? json_encode($dbRequireParams, JSON_UNESCAPED_UNICODE) : '';
                break;
        }
        $dbData = $data['dbData'];
        // 判断route.name是否存在
        if ($type == RouteDataType::ROUTE && ($routeName = $data['dbData']['name'])) {
            $where = [['name', '=', $routeName]];
            if (isset($data['dbData']['id'])) {
                $where[] = ['id', '<>', $data['dbData']['id']];
            }
            $hasExistData = $model->getOwnRowToArray($where, ['id', 'desc', 'state']);
            if ($hasExistData) {
                /** @var State $hasExistDataStateObj */
                $hasExistDataStateObj = State::byValue($hasExistData['state']);
                $msg = "路由别名已存在，与路由【描述:{$hasExistData['desc']}|状态:" . $hasExistDataStateObj->getDesc() . "】冲突";
                throw_error_json($msg);
            }
        }
        $data['model'] = $model;
    }

    // 校验并返回新规则
    private function checkRouteRule($rule, string $desc): string {
        $rule = handle_route_rule($rule);
        if (!$rule) {
            throw_error_json("{$desc}错误");
        }
        return $rule;
    }

}